/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package algebra;



import algebra.parallel.MatrixRunnable;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author max
 *
 * Matrix Klasse zur parallelen Berechnung von Matriz-Vektor-Produkten
 *
 * Idee: die Matrix wird in soviele Zeilen geteilt, wie Kerne zur verfügung
 * stehen. Jeder Kern kann dann das Matrix-Vektor-Produkt seiner Zeilen
 * berechnen
 *
 */
public class PMatrix_W0 {

    ArrayList<ArrayList<ArrayList<MatrixItem>>> matrixset; //Daten
   
    int blabla = 0;
    int rows,
            columns;  // Zeilen, Spaltem
    int nproc; //Anzahl der Prozessoren
    int[] nproc_rows; //Anzahl der Zeilen die der i-te Kern berechnen soll

    public PMatrix_W0(int rows, int columns) {

        
        this.rows = rows;
        this.columns = columns;
        nproc = Runtime.getRuntime().availableProcessors();
       
        
        if (rows < nproc || rows < 8500) {
            nproc = 1;
        }
         
        
        
        initPMatrix();

    }

    private void initPMatrix() {

        //Matrix initialisieren

        nproc_rows = new int[nproc];
        
        //Zeilen in gleich grosse Teile teilen
        int first_spread = (int) ((double) (rows) / (double) (nproc));
        //Übrige Zeilen berechnen
        int rows_left = rows % nproc;

        for (int i = 0; i < nproc; i++) {

            //Zeilen den Prozessoren zuordnen
            nproc_rows[i] = first_spread;

        }
        int tmp = 0;
        //die übrigen Zeilen verteilen
        while (rows_left > 0) {

            nproc_rows[tmp++]++; //geht weil number_rows_proc > rows_left
            rows_left--;

        }

        //ArrayListen initialisieren

        matrixset = new ArrayList<>(nproc);
        for (int i = 0; i < nproc; i++) {
            matrixset.add(new ArrayList<ArrayList<MatrixItem>>(nproc_rows[i]));
            for (int j = 0; j < nproc_rows[i]; j++) {
                matrixset.get(i).add(new ArrayList<MatrixItem>());
                

            }
        }

//        matrixitems = new ArrayList<>(nproc);
//        for (int i = 0; i < nproc; i++) {
//            matrixitems.add(new ArrayList<ArrayList<MatrixItem>>(nproc_rows[i]));
//            for (int j = 0; j < nproc_rows[i]; j++) {
//                matrixitems.get(i).add(new ArrayList<MatrixItem>(columns));
//                for (int k = 0; k < columns; k++) {
//                    matrixset.get(i).get(j).add(0d);
//                }
//
//            }
//        }
        
        

    }

    public double get(int i, int j) {

        //i,j Eintrag bekommen

//        int k = 0;
//
//        while (i >= matrixset.get(k).size()) {
//
//            i -= matrixset.get(k).size();
//            k++;
//        }
//
//        return matrixset.get(k).get(i).get(j);

        int k = 0;

        while (i >= matrixset.get(k).size()) {

            i -= matrixset.get(k).size();
            k++;
        }

        MatrixItem m = null;
        for(int g = 0; g < matrixset.get(k).get(i).size(); g++){
        if(matrixset.get(k).get(i).get(g).getj() == j){
        
        m = matrixset.get(k).get(i).get(g);
        break;
        }
        
        }
        if(m == null){ 
           // System.out.println(++blabla);
            return 0;}
        else return m.getvalue();

    }

    public double set(int i, int j, double d) {

        //i,j Eintrag setzen

//        int k = 0;
//
//        while (i >= matrixset.get(k).size()) {
//
//            i -= matrixset.get(k).size();
//            k++;
//        }
//
//        return matrixset.get(k).get(i).set(j, d);

        int oi = i;
        int k = 0;

        while (i >= matrixset.get(k).size()) {

            i -= matrixset.get(k).size();
            k++;
        }

        //return matrixset.get(k).get(i).set(j, d);
        MatrixItem m = null;
        for(int g = 0; g < matrixset.get(k).get(i).size(); g++){
        if(matrixset.get(k).get(i).get(g).getj() == j){
        
        m = matrixset.get(k).get(i).get(g);
        m.setvalue(d);
        break;
        }
        
        }

        if(m!=null) return m.getvalue();
        else{
        
            matrixset.get(k).get(i).add(new MatrixItem(oi, j, d));
            return d;
        
        }
        
    }

    public Vector vec_mul(Vector vec) {

        //Matrix-Vektor-Produkt

        //return wert
        Vector ret_vec = new Vector(rows);
        //return vektor in zeilen unterteilt
        ArrayList<Vector> nproc_vec = new ArrayList<>(nproc);
        
        //Multi-Threading
        ArrayList<Thread> threads = new ArrayList<>(nproc);
        for (int i = 0; i < nproc; i++) {
            nproc_vec.add(new Vector(nproc_rows[i]));
            //Threads und Runnable Objekt erstellen
           // threads.add(new Thread(new MatrixRunnable(matrixset.get(i), vec, nproc_vec.get(i))));
            //und starten
            threads.get(i).start();
        }
        for (int i = 0; i < nproc; i++) {
            try {
                //Auf langsamsten Thread warten
                threads.get(i).join();
            } catch (InterruptedException ex) {
                Logger.getLogger(PMatrix_W0.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        //Teilvektoren zusammensetzen
        int j = 0;
        for (int i = 0; i < nproc; i++) {
            ret_vec.set(j, nproc_vec.get(i));
            j += nproc_rows[i];
        }

        //fertig
        return ret_vec;
    }
    
    
     public Vector vec_scal_mul(Vector vec) {

        //Matrix-Vektor-Produkt
         
        //return wert
        Vector ret_vec = new Vector(rows);
        //return vektor in zeilen unterteilt
        ArrayList<Vector> nproc_vec = new ArrayList<>(nproc);
        
        //Multi-Threading
        int vec_start = 0;
        ArrayList<Thread> threads = new ArrayList<>(nproc);
        for (int i = 0; i < nproc; i++) {
            nproc_vec.add(new Vector(nproc_rows[i]));
            //Threads und Runnable Objekt erstellen
            threads.add(new Thread(new MatrixRunnable(matrixset.get(i), vec, nproc_vec.get(i),vec_start)));
            vec_start+=nproc_rows[i];
            //und starten
           // threads.get(i).start();
        }
        
//          long currentTimeMillis = System.currentTimeMillis();
//        System.out.println("Started at " + currentTimeMillis);
        //Starting Them
        for (Thread thread : threads) {
            thread.start();
        }
        //Joining Them
        for (Thread thread : threads) {
             try {
//                 long currentTimeMillis2 = System.currentTimeMillis();
//                  if(currentTimeMillis2 - currentTimeMillis>1000){
//                  
//                  int ifgfdgv = 1;
//                  }
                 thread.join();
             } catch (InterruptedException ex) {
                // System.out.println("sngkdjf");
                 Logger.getLogger(PMatrix_W0.class.getName()).log(Level.SEVERE, null, ex);
             }
        }
         //System.out.println("");
//        long currentTimeMillis2 = System.currentTimeMillis();
//        System.out.println("Ended at " + currentTimeMillis2);
//        System.out.println("Diff : " +( currentTimeMillis2 - currentTimeMillis));
//         System.out.println("");
//         System.out.println("");
        
        
//        for (int i = 0; i < nproc; i++) {
//            try {
//                //Auf langsamsten Thread warten
//                threads.get(i).join();
//            } catch (InterruptedException ex) {
//                Logger.getLogger(PMatrix.class.getName()).log(Level.SEVERE, null, ex);
//            }
//        }
        //Teilvektoren zusammensetzen
        int j = 0;
        for (int i = 0; i < nproc; i++) {
            ret_vec.set(j, nproc_vec.get(i));
            j += nproc_rows[i];
        }

        //fertig
        return ret_vec;
    }

    public String toString() {

        String s = "";

        for (int i = 0; i < rows; i++) {

            for (int j = 0; j < columns; j++) {

                s += Double.toString(this.get(i, j)) + " ";

            }

            s += " \r\n";
        }

        return s;


    }
    
    
        public void toString2() {

       

        for (int i = 0; i < rows; i++) {

            String s = "";
            
            for (int j = 0; j < columns; j++) {

                s += Double.toString(this.get(i, j)) + " ";

            }

            System.out.println(s);
        }

        


    }
    
    
    public int getrows(){return this.rows;}
    public int getcolumns(){return this.columns;}

    public static void main(String[] args) {


//        PMatrix a = new PMatrix(6000, 6000);
//        int ij = 0;
//
//        for (int i = 0; i < 6000; i++) {
//            for (int j = 0; j < 6000; j++) {
//
//                a.set(i, j, ++ij);
//
//            }
//        }
//        Vector b = new Vector(6000, 1);
////        b.set(1, 2);
////        b.set(2, 3);
//
//        long syst = System.currentTimeMillis();
//        Vector c = null;
//        for (int i = 0; i < 1; i++) {
//
//            c = a.vec_mul(b);
//        }
//
//        syst = System.currentTimeMillis() - syst;
//        System.out.println("Zeit: " + Long.toString(syst));
//
////        System.out.println(a);
////        System.out.println(b);
////        System.out.println(c);
        
        PMatrix_W0 a = new PMatrix_W0(20, 20);
        
        a.set(1, 1, 1);
        a.set(12, 16, 3);
        a.set(18, 17, 3);
        a.set(19, 19, 4);
        a.set(19, 18, 2);
        
        System.out.println(a);
        
        Vector b = new Vector(20, 2);
        
        System.out.println(a.vec_scal_mul(b));

    }
    
    public PMatrix_W0 clone(){
    
      PMatrix_W0 newmat = new PMatrix_W0(rows, columns);
        
        for(int i = 0; i < rows; i++){
            for(int j = 0; j < columns; j++){
                
                newmat.set(i, j, this.get(i, j));
                
            }
        }
        
    
    return newmat;
    
    }
}
